#include "stdafx.h"

#pragma managed
#include "MethodType.h"
#include "..\Database.h"
using namespace System::Text;
using namespace System::Diagnostics;

namespace ManagedAPI
{
	// static method
	MethodType^ MethodType::Create(const UdbEntity& entity)
	{
		Debug::Assert(entity != nullptr);
		if (entity == nullptr)
		{
			return nullptr;
		}

		char* keystr = udbEntityNameUnique(entity);
		String^ key = gcnew String(keystr);

		MethodType^ result = nullptr;
		if (sMethodList->ContainsKey(key))
		{
			result = sMethodList[key];
			Debug::Assert(result != nullptr);
		}
		else
		{
			result = gcnew MethodType(entity);
			sMethodList->Add(key, result);
		}
		return result;
	}


	void MethodType::Clear()
	{
		sMethodList->Clear();
	}

	MethodType::MethodType(const UdbEntity& entity)
		: mIsConstructor(-1),
		Entity(entity)
	{
		mAttributes = Attributes;

	}

	array<MethodType^>^ MethodType::GetOverrides()
	{
		if (mOverRides == nullptr)
		{
			mOverRides = gcnew List<MethodType^>();

			UdbEntity thisEnt = GetUdbEntity();
			UdbReference* refs = nullptr;
			int unique = 1;
			int numRefs = udbEntityRefs(thisEnt, "Overriddenby", "Member Function", unique, &refs );

			if (numRefs > 0)
			{
				for (int i = 0; i < numRefs; i++)
				{
					UdbReference ref = refs[i];
					Debug::Assert(ref != nullptr);
					UdbEntity entity = udbReferenceEntity(ref);
					Debug::Assert(entity != nullptr);
					MethodType^ ent = MethodType::Create(entity);
					Debug::Assert(ent != nullptr);
					mOverRides->Add(ent);
				}
			}
			refs = nullptr;
		}
		return mOverRides->ToArray();
	}
	String^ MethodType::ToString()
	{
		StringBuilder^ sb = gcnew StringBuilder();

		sb->Append(Attributes);
		sb->Append(" ");
		String^ typeStr = this->TypeText;
		if (typeStr != nullptr)
		{
			sb->Append(typeStr);
			sb->Append(" ");
		}
		sb->Append(this->NameLong);
		sb->Append( GetParameters(true) );

		return sb->ToString()->TrimStart();
	}
	String^ MethodType::ToString(bool printFullName, bool printParameterNames)
	{
		StringBuilder^ sb = gcnew StringBuilder();

		sb->Append(Attributes);
		sb->Append(" ");
		String^ typeStr = this->TypeText;
		if (typeStr != nullptr)
		{
			sb->Append(typeStr);
			sb->Append(" ");
		}
		
		if (printFullName)
			sb->Append(this->NameLong);
		else
			sb->Append(this->NameShort);

		sb->Append( GetParameters(printParameterNames) );

		return sb->ToString()->TrimStart();
	}
	array<Entity^>^ MethodType::Parameters::get()
	{
		if (mParameters == nullptr)
		{
			UdbEntity thisEnt = GetUdbEntity();
			UdbReference* refs = nullptr;
			int unique = 1;
			int numRefs = udbEntityRefs(thisEnt, "define", "Parameter", unique, &refs );

			if (numRefs > 0)
			{
				mParameters = gcnew array<Entity^>(numRefs);
				for (int i = 0; i < numRefs; i++)
				{
					UdbReference ref = refs[i];
					Debug::Assert(ref != nullptr);
					UdbEntity entity = udbReferenceEntity(ref);
					Debug::Assert(entity != nullptr);
					Entity^ ent = Entity::Create(entity);
					Debug::Assert(ent != nullptr);
					mParameters[i] = ent;
				}
			}
			refs = nullptr;
		}
		return mParameters;
	}

	String^ MethodType::GetParameters(bool getParameterNames)
	{
		char* paramsList = 0;
		int getNames = getParameterNames ? 1 : 0;

		UdbEntity entity = GetUdbEntity();
		// From the scitools website (i.e. It's not in the docs:
		// "This returns true if an entity may have parameters (ie, a function). 
		// The text that is returned is temporary and must be copied immediately
		// and must not be freed by the user."
		int hasParams = udbEntityParameters(entity, &paramsList, getNames );

		StringBuilder^ sb = gcnew StringBuilder("(");

		if ( (hasParams != 0) && (paramsList != 0) )
		{
			String^ tempParams = gcnew String(paramsList);
			sb->Append(tempParams);
		}
		sb->Append(")");

		return sb->ToString();
	}

	String^ MethodType::Attributes::get()
	{
		if ( String::IsNullOrEmpty(mAttributes) == true )
		{
			StringBuilder^ sb  = gcnew StringBuilder();
			String^ kindStr = this->KindName;
			if (kindStr->Contains("Virtual"))
			{
				mAttributesFlag |= (int)System::Reflection::MethodAttributes::Virtual;
				if (kindStr->Contains("Pure"))
				{
					sb->Append("virtual pure ");
					mAttributesFlag |= (int)System::Reflection::MethodAttributes::Abstract;
				} 
				else
				{
					sb->Append("virtual ");
				}
			}
			if (kindStr->Contains("Static"))
			{
				sb->Append("static ");
				mAttributesFlag |= (int)System::Reflection::MethodAttributes::Static;
			}
			if (kindStr->Contains("Const"))
			{
				sb->Append("const ");
			}
			if (kindStr->Contains("Private"))
			{
				mAttributesFlag |= (int)System::Reflection::MethodAttributes::Private;
			}
			if (kindStr->Contains("Public"))
			{
				mAttributesFlag |= (int)System::Reflection::MethodAttributes::Public;
			}

			if (kindStr->Contains("Protected"))
				mIsProtected = 1;
			else
				mIsProtected = 0;

			mAttributes = sb->ToString()->TrimEnd();
		}

		return mAttributes;
	}
	bool MethodType::IsAbstract::get()
	{
		return ((mAttributesFlag & (int)System::Reflection::MethodAttributes::Abstract) == (int)System::Reflection::MethodAttributes::Abstract);
	}
	bool MethodType::IsVirtual::get()
	{
		return ((mAttributesFlag & (int)System::Reflection::MethodAttributes::Virtual) == (int)System::Reflection::MethodAttributes::Virtual);
	}
	bool MethodType::IsConstructor::get()
	{
		// Utilizes Lazy Initialization
		if (mIsConstructor == -1)
		{
			mIsConstructor = 0;
			String^ fullname = this->NameLong;
			array<Char>^ chars = {':'};
			array<String^>^ names = fullname->Split(chars, StringSplitOptions::RemoveEmptyEntries);
			if (names->Length >= 2)
			{
				int n = names->Length - 1;
				if (names[n] == names[n-1])
				{
					mIsConstructor = 1;
				}
			}
		}

		return (mIsConstructor == 1);
	}
	bool MethodType::IsPrivate::get()
	{
		return ((mAttributesFlag & (int)System::Reflection::MethodAttributes::Private) == (int)System::Reflection::MethodAttributes::Private);
	}
	bool MethodType::IsPublic::get()
	{
		return ((mAttributesFlag & (int)System::Reflection::MethodAttributes::Public) == (int)System::Reflection::MethodAttributes::Public);
	}
	bool MethodType::IsProtected::get()
	{
		return (mIsProtected == 1);
	}
	bool MethodType::IsStatic::get()
	{
		return ((mAttributesFlag & (int)System::Reflection::MethodAttributes::Static) == (int)System::Reflection::MethodAttributes::Static);
	}
	bool MethodType::IsInline::get()
	{
		bool result = false;
		if (this->LineDeclareStart == 0)
		{
			result = true;
		}
		return result;
	}
	template<typename T> array<T^>^ MethodType::GetTypes(String^ refkinds, String^ entkinds)
	{
		UdbEntity thisEnt  = GetUdbEntity(); // Get a non-cached UdbEntity
		char* tempRefKinds = Util::StringToChar(refkinds);
		char* tempEntKinds = Util::StringToChar(entkinds);
		int unique         = 1;
		UdbReference* refs = nullptr;
		int numRefs = udbEntityRefs(thisEnt, tempRefKinds, tempEntKinds, unique, &refs);
		// free the native strings
		Util::FreeNativeString(tempRefKinds);
		tempRefKinds = nullptr;
		Util::FreeNativeString(tempEntKinds);
		tempEntKinds = nullptr;

		List<T^>^ result = gcnew List<T^>();
		if ( (numRefs > 0) && (refs != nullptr) )
		{
			for (int i = 0; i < numRefs; i++)
			{
				UdbReference ref = refs[i];
				Debug::Assert(ref != nullptr);
				UdbEntity entity = udbReferenceEntity(ref);
				Debug::Assert(entity != nullptr);
				T^ ent = T::Create(entity);
				Debug::Assert(ent != nullptr, "Unable to create Type");
				result->Add(ent);
			}
		}
		udbListReferenceFree(refs);
		refs = nullptr;

		return result->ToArray();
	}
	void MethodType::GetLexLineNumbers(int% start, int% end)
	{
		if (this->IsInline == true)
		{
			start = this->LineDefineStart;
			end   = this->LineDefineEnd;
			return;
		}
		Reference^ ref = GetRef();
		String^ filename = ref->File->NameSimple;
		FileEntity^ fileEnt = Database::LookupFileEntity(filename);
		Lexer^ lexer = fileEnt->GetLexer();
		Lexeme^ lexm = lexer->GetLexeme(this->LineDeclareStart, ref->Column);
		
		int tempStart = lexm->LineBegin;

		if (! String::IsNullOrEmpty(this->TypeText))
		{
			array<Char>^ chars = {' ', '\t'};
			array<String^>^ splits = this->TypeText->Split(chars, StringSplitOptions::RemoveEmptyEntries);

			if (splits->Length > 0)
			{
				String^ firstTypeWord = splits[0];
				Lexeme^ typelex = lexm->Previous;
				while ((typelex->Text != ";") && 
					(typelex->Token != UdbToken::Udb_tokenComment))
				{
					if (typelex->Text == firstTypeWord)
					{
						tempStart = typelex->LineBegin;
						break;
					}
					typelex = typelex->Previous;
				}
			}
		}

		
		while (lexm->Text != ";")
		{
			lexm = lexm->Next;
		}
		
		int tempEnd = lexm->LineEnd;

		start = tempStart;
		end = tempEnd;
	}
}